/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the  "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * $Id$
 */
package wx.xml.xalan.xalan.transformer;

import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.DTDHandler;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.ext.DeclHandler;
import org.xml.sax.ext.LexicalHandler;

import java.io.IOException;

import javax.xml.transform.Result;
import javax.xml.transform.Transformer;
import javax.xml.transform.sax.TransformerHandler;

import wx.xml.xalan.xalan.res.XSLMessages;
import wx.xml.xalan.xalan.res.XSLTErrorResources;
import wx.xml.xalan.xml.dtm.DTM;
import wx.xml.xalan.xml.dtm.ref.IncrementalSAXSource_Filter;
import wx.xml.xalan.xml.dtm.ref.sax2dtm.SAX2DTM;
import wx.xml.xalan.xml.serializer.SerializationHandler;
import wx.xml.xalan.xpath.XPathContext;


/**
 * A TransformerHandler
 * listens for SAX ContentHandler parse events and transforms
 * them to a Result.
 */
public class TransformerHandlerImpl
    implements EntityResolver, DTDHandler, ContentHandler, ErrorHandler,
    LexicalHandler, TransformerHandler, DeclHandler {
    /**
     * Set to true for diagnostics output.
     */
    private static boolean DEBUG = false;
    /**
     * The flag for the setting of the optimize feature;
     */
    private final boolean m_optimizer;
    /**
     * The flag for the setting of the incremental feature;
     */
    private final boolean m_incremental;
    /**
     * The flag for the setting of the source_location feature;
     */
    private final boolean m_source_location;

    ////////////////////////////////////////////////////////////////////
    // Constructors.
    ////////////////////////////////////////////////////////////////////
    /**
     * The Document Table Instance we are transforming.
     */
    DTM m_dtm;
    private boolean m_insideParse = false;
    /**
     * The transformer this will use to transform a
     * source tree into a result tree.
     */
    private TransformerImpl m_transformer;

    ////////////////////////////////////////////////////////////////////
    // Implementation of javax.xml.transform.sax.TransformerHandler.
    ////////////////////////////////////////////////////////////////////
    /**
     * The system ID to use as a base for relative URLs.
     */
    private String m_baseSystemID;
    /**
     * The result for the transformation.
     */
    private Result m_result = null;
    /**
     * The locator for this TransformerHandler.
     */
    private Locator m_locator = null;
    /**
     * The entity resolver to aggregate to.
     */
    private EntityResolver m_entityResolver = null;

    ////////////////////////////////////////////////////////////////////
    // Implementation of org.xml.sax.EntityResolver.
    ////////////////////////////////////////////////////////////////////
    /**
     * The DTD handler to aggregate to.
     */
    private DTDHandler m_dtdHandler = null;

    ////////////////////////////////////////////////////////////////////
    // Implementation of org.xml.sax.DTDHandler.
    ////////////////////////////////////////////////////////////////////
    /**
     * The content handler to aggregate to.
     */
    private ContentHandler m_contentHandler = null;
    /**
     * The error handler to aggregate to.
     */
    private ErrorHandler m_errorHandler = null;

    ////////////////////////////////////////////////////////////////////
    // Implementation of org.xml.sax.ContentHandler.
    ////////////////////////////////////////////////////////////////////
    /**
     * The lexical handler to aggregate to.
     */
    private LexicalHandler m_lexicalHandler = null;
    /**
     * The decl handler to aggregate to.
     */
    private DeclHandler m_declHandler = null;

    /**
     * Construct a TransformerHandlerImpl.
     *
     * @param transformer  Non-null reference to the Xalan transformer impl.
     * @param doFragment   True if the result should be a document fragement.
     * @param baseSystemID The system ID to use as the base for relative URLs.
     */
    public TransformerHandlerImpl(TransformerImpl transformer,
                                  boolean doFragment, String baseSystemID) {

        super();

        m_transformer = transformer;
        m_baseSystemID = baseSystemID;

        XPathContext xctxt = transformer.getXPathContext();
        DTM          dtm   = xctxt.getDTM(null, true, transformer, true, true);

        m_dtm = dtm;
        dtm.setDocumentBaseURI(baseSystemID);

        m_contentHandler = dtm.getContentHandler();
        m_dtdHandler = dtm.getDTDHandler();
        m_entityResolver = dtm.getEntityResolver();
        m_errorHandler = dtm.getErrorHandler();
        m_lexicalHandler = dtm.getLexicalHandler();
        m_incremental = transformer.getIncremental();
        m_optimizer = transformer.getOptimize();
        m_source_location = transformer.getSource_location();
    }

    /**
     * Do what needs to be done to shut down the CoRoutine management.
     */
    protected void clearCoRoutine() {
        clearCoRoutine(null);
    }

    /**
     * Do what needs to be done to shut down the CoRoutine management.
     */
    protected void clearCoRoutine(SAXException ex) {
        if (null != ex)
            m_transformer.setExceptionThrown(ex);

        if (m_dtm instanceof SAX2DTM) {
            if (DEBUG)
                System.err.println("In clearCoRoutine...");
            try {
                SAX2DTM sax2dtm = ((SAX2DTM) m_dtm);
                if (null != m_contentHandler
                    && m_contentHandler instanceof IncrementalSAXSource_Filter) {
                    IncrementalSAXSource_Filter sp =
                        (IncrementalSAXSource_Filter) m_contentHandler;
                    // This should now be all that's needed.
                    sp.deliverMoreNodes(false);
                }

                sax2dtm.clearCoRoutine(true);
                m_contentHandler = null;
                m_dtdHandler = null;
                m_entityResolver = null;
                m_errorHandler = null;
                m_lexicalHandler = null;
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }

            if (DEBUG)
                System.err.println("...exiting clearCoRoutine");
        }
    }

    /**
     * Enables the user of the TransformerHandler to set the
     * to set the Result for the transformation.
     *
     * @param result A Result instance, should not be null.
     * @throws IllegalArgumentException if result is invalid for some reason.
     */
    public void setResult(Result result) throws IllegalArgumentException {

        if (null == result)
            throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_RESULT_NULL, null)); //"result should not be null");

        try {
//      ContentHandler handler =
//        m_transformer.createResultContentHandler(result);
//      m_transformer.setContentHandler(handler);
            SerializationHandler xoh =
                m_transformer.createSerializationHandler(result);
            m_transformer.setSerializationHandler(xoh);
        } catch (javax.xml.transform.TransformerException te) {
            throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_RESULT_COULD_NOT_BE_SET, null)); //"result could not be set");
        }

        m_result = result;
    }

    /**
     * Get the base ID (URI or system ID) from where relative
     * URLs will be resolved.
     *
     * @return The systemID that was set with {@link #setSystemId}.
     */
    public String getSystemId() {
        return m_baseSystemID;
    }

    /**
     * Set the base ID (URI or system ID) from where relative
     * URLs will be resolved.
     *
     * @param systemID Base URI for the source tree.
     */
    public void setSystemId(String systemID) {
        m_baseSystemID = systemID;
        m_dtm.setDocumentBaseURI(systemID);
    }

    /**
     * Get the Transformer associated with this handler, which
     * is needed in order to set parameters and output properties.
     *
     * @return The Transformer associated with this handler
     */
    public Transformer getTransformer() {
        return m_transformer;
    }

    /**
     * Filter an external entity resolution.
     *
     * @param publicId The entity's public identifier, or null.
     * @param systemId The entity's system identifier.
     * @return A new InputSource or null for the default.
     * @throws IOException
     * @throws SAXException        The client may throw
     *                             an exception during processing.
     * @throws java.io.IOException The client may throw an
     *                             I/O-related exception while obtaining the
     *                             new InputSource.
     * @see org.xml.sax.EntityResolver#resolveEntity
     */
    public InputSource resolveEntity(String publicId, String systemId)
        throws SAXException, IOException {

        if (m_entityResolver != null) {
            return m_entityResolver.resolveEntity(publicId, systemId);
        } else {
            return null;
        }
    }

    /**
     * Filter a notation declaration event.
     *
     * @param name     The notation name.
     * @param publicId The notation's public identifier, or null.
     * @param systemId The notation's system identifier, or null.
     * @throws SAXException The client may throw
     *                      an exception during processing.
     * @see org.xml.sax.DTDHandler#notationDecl
     */
    public void notationDecl(String name, String publicId, String systemId)
        throws SAXException {

        if (m_dtdHandler != null) {
            m_dtdHandler.notationDecl(name, publicId, systemId);
        }
    }

    ////////////////////////////////////////////////////////////////////
    // Implementation of org.xml.sax.ErrorHandler.
    ////////////////////////////////////////////////////////////////////

    /**
     * Filter an unparsed entity declaration event.
     *
     * @param name         The entity name.
     * @param publicId     The entity's public identifier, or null.
     * @param systemId     The entity's system identifier, or null.
     * @param notationName The name of the associated notation.
     * @throws SAXException The client may throw
     *                      an exception during processing.
     * @see org.xml.sax.DTDHandler#unparsedEntityDecl
     */
    public void unparsedEntityDecl(
        String name, String publicId, String systemId, String notationName)
        throws SAXException {

        if (m_dtdHandler != null) {
            m_dtdHandler.unparsedEntityDecl(name, publicId, systemId, notationName);
        }
    }

    /**
     * Filter a new document locator event.
     *
     * @param locator The document locator.
     * @see org.xml.sax.ContentHandler#setDocumentLocator
     */
    public void setDocumentLocator(Locator locator) {

        if (DEBUG)
            System.out.println("TransformerHandlerImpl#setDocumentLocator: "
                + locator.getSystemId());

        this.m_locator = locator;

        if (null == m_baseSystemID) {
            setSystemId(locator.getSystemId());
        }

        if (m_contentHandler != null) {
            m_contentHandler.setDocumentLocator(locator);
        }
    }

    /**
     * Filter a start document event.
     *
     * @throws SAXException The client may throw
     *                      an exception during processing.
     * @see org.xml.sax.ContentHandler#startDocument
     */
    public void startDocument() throws SAXException {

        if (DEBUG)
            System.out.println("TransformerHandlerImpl#startDocument");

        m_insideParse = true;

        // Thread listener = new Thread(m_transformer);

        if (m_contentHandler != null) {
            //m_transformer.setTransformThread(listener);
            if (m_incremental) {
                m_transformer.setSourceTreeDocForThread(m_dtm.getDocument());

                int cpriority = Thread.currentThread().getPriority();

                // runTransformThread is equivalent with the 2.0.1 code,
                // except that the Thread may come from a pool.
                m_transformer.runTransformThread(cpriority);
            }

            // This is now done _last_, because IncrementalSAXSource_Filter
            // will immediately go into a "wait until events are requested"
            // pause. I believe that will close our timing window.
            // %REVIEW%
            m_contentHandler.startDocument();
        }

        //listener.setDaemon(false);
        //listener.start();

    }

    ////////////////////////////////////////////////////////////////////
    // Implementation of org.xml.sax.ext.LexicalHandler.
    ////////////////////////////////////////////////////////////////////

    /**
     * Filter an end document event.
     *
     * @throws SAXException The client may throw
     *                      an exception during processing.
     * @see org.xml.sax.ContentHandler#endDocument
     */
    public void endDocument() throws SAXException {

        if (DEBUG)
            System.out.println("TransformerHandlerImpl#endDocument");

        m_insideParse = false;

        if (m_contentHandler != null) {
            m_contentHandler.endDocument();
        }

        if (m_incremental) {
            m_transformer.waitTransformThread();
        } else {
            m_transformer.setSourceTreeDocForThread(m_dtm.getDocument());
            m_transformer.run();
        }
   /* Thread transformThread = m_transformer.getTransformThread();

    if (null != transformThread)
    {
      try
      {

        // This should wait until the transformThread is considered not alive.
        transformThread.join();

        if (!m_transformer.hasTransformThreadErrorCatcher())
        {
          Exception e = m_transformer.getExceptionThrown();

          if (null != e)
            throw new org.xml.sax.SAXException(e);
        }

        m_transformer.setTransformThread(null);
      }
      catch (InterruptedException ie){}
    }*/
    }

    /**
     * Filter a start Namespace prefix mapping event.
     *
     * @param prefix The Namespace prefix.
     * @param uri    The Namespace URI.
     * @throws SAXException The client may throw
     *                      an exception during processing.
     * @see org.xml.sax.ContentHandler#startPrefixMapping
     */
    public void startPrefixMapping(String prefix, String uri)
        throws SAXException {

        if (DEBUG)
            System.out.println("TransformerHandlerImpl#startPrefixMapping: "
                + prefix + ", " + uri);

        if (m_contentHandler != null) {
            m_contentHandler.startPrefixMapping(prefix, uri);
        }
    }

    /**
     * Filter an end Namespace prefix mapping event.
     *
     * @param prefix The Namespace prefix.
     * @throws SAXException The client may throw
     *                      an exception during processing.
     * @see org.xml.sax.ContentHandler#endPrefixMapping
     */
    public void endPrefixMapping(String prefix) throws SAXException {

        if (DEBUG)
            System.out.println("TransformerHandlerImpl#endPrefixMapping: "
                + prefix);

        if (m_contentHandler != null) {
            m_contentHandler.endPrefixMapping(prefix);
        }
    }

    /**
     * Filter a start element event.
     *
     * @param uri       The element's Namespace URI, or the empty string.
     * @param localName The element's local name, or the empty string.
     * @param qName     The element's qualified (prefixed) name, or the empty
     *                  string.
     * @param atts      The element's attributes.
     * @throws SAXException The client may throw
     *                      an exception during processing.
     * @see org.xml.sax.ContentHandler#startElement
     */
    public void startElement(
        String uri, String localName, String qName, Attributes atts)
        throws SAXException {

        if (DEBUG)
            System.out.println("TransformerHandlerImpl#startElement: " + qName);

        if (m_contentHandler != null) {
            m_contentHandler.startElement(uri, localName, qName, atts);
        }
    }

    /**
     * Filter an end element event.
     *
     * @param uri       The element's Namespace URI, or the empty string.
     * @param localName The element's local name, or the empty string.
     * @param qName     The element's qualified (prefixed) name, or the empty
     *                  string.
     * @throws SAXException The client may throw
     *                      an exception during processing.
     * @see org.xml.sax.ContentHandler#endElement
     */
    public void endElement(String uri, String localName, String qName)
        throws SAXException {

        if (DEBUG)
            System.out.println("TransformerHandlerImpl#endElement: " + qName);

        if (m_contentHandler != null) {
            m_contentHandler.endElement(uri, localName, qName);
        }
    }

    /**
     * Filter a character data event.
     *
     * @param ch     An array of characters.
     * @param start  The starting position in the array.
     * @param length The number of characters to use from the array.
     * @throws SAXException The client may throw
     *                      an exception during processing.
     * @see org.xml.sax.ContentHandler#characters
     */
    public void characters(char ch[], int start, int length) throws SAXException {

        if (DEBUG)
            System.out.println("TransformerHandlerImpl#characters: " + start + ", "
                + length);

        if (m_contentHandler != null) {
            m_contentHandler.characters(ch, start, length);
        }
    }

    /**
     * Filter an ignorable whitespace event.
     *
     * @param ch     An array of characters.
     * @param start  The starting position in the array.
     * @param length The number of characters to use from the array.
     * @throws SAXException The client may throw
     *                      an exception during processing.
     * @see org.xml.sax.ContentHandler#ignorableWhitespace
     */
    public void ignorableWhitespace(char ch[], int start, int length)
        throws SAXException {

        if (DEBUG)
            System.out.println("TransformerHandlerImpl#ignorableWhitespace: "
                + start + ", " + length);

        if (m_contentHandler != null) {
            m_contentHandler.ignorableWhitespace(ch, start, length);
        }
    }

    ////////////////////////////////////////////////////////////////////
    // Implementation of org.xml.sax.ext.DeclHandler.
    ////////////////////////////////////////////////////////////////////

    /**
     * Filter a processing instruction event.
     *
     * @param target The processing instruction target.
     * @param data   The text following the target.
     * @throws SAXException The client may throw
     *                      an exception during processing.
     * @see org.xml.sax.ContentHandler#processingInstruction
     */
    public void processingInstruction(String target, String data)
        throws SAXException {

        if (DEBUG)
            System.out.println("TransformerHandlerImpl#processingInstruction: "
                + target + ", " + data);

        if (m_contentHandler != null) {
            m_contentHandler.processingInstruction(target, data);
        }
    }

    /**
     * Filter a skipped entity event.
     *
     * @param name The name of the skipped entity.
     * @throws SAXException The client may throw
     *                      an exception during processing.
     * @see org.xml.sax.ContentHandler#skippedEntity
     */
    public void skippedEntity(String name) throws SAXException {

        if (DEBUG)
            System.out.println("TransformerHandlerImpl#skippedEntity: " + name);

        if (m_contentHandler != null) {
            m_contentHandler.skippedEntity(name);
        }
    }

    /**
     * Filter a warning event.
     *
     * @param e The nwarning as an exception.
     * @throws SAXException The client may throw
     *                      an exception during processing.
     * @see org.xml.sax.ErrorHandler#warning
     */
    public void warning(SAXParseException e) throws SAXException {
        // This is not great, but we really would rather have the error
        // handler be the error listener if it is a error handler.  Coroutine's fatalError
        // can't really be configured, so I think this is the best thing right now
        // for error reporting.  Possibly another JAXP 1.1 hole.  -sb
        javax.xml.transform.ErrorListener errorListener = m_transformer.getErrorListener();
        if (errorListener instanceof ErrorHandler) {
            ((ErrorHandler) errorListener).warning(e);
        } else {
            try {
                errorListener.warning(new javax.xml.transform.TransformerException(e));
            } catch (javax.xml.transform.TransformerException te) {
                throw e;
            }
        }
    }

    /**
     * Filter an error event.
     *
     * @param e The error as an exception.
     * @throws SAXException The client may throw
     *                      an exception during processing.
     * @see org.xml.sax.ErrorHandler#error
     */
    public void error(SAXParseException e) throws SAXException {
        // %REVIEW% I don't think this should be called.  -sb
        // clearCoRoutine(e);

        // This is not great, but we really would rather have the error
        // handler be the error listener if it is a error handler.  Coroutine's fatalError
        // can't really be configured, so I think this is the best thing right now
        // for error reporting.  Possibly another JAXP 1.1 hole.  -sb
        javax.xml.transform.ErrorListener errorListener = m_transformer.getErrorListener();
        if (errorListener instanceof ErrorHandler) {
            ((ErrorHandler) errorListener).error(e);
            if (null != m_errorHandler)
                m_errorHandler.error(e); // may not be called.
        } else {
            try {
                errorListener.error(new javax.xml.transform.TransformerException(e));
                if (null != m_errorHandler)
                    m_errorHandler.error(e); // may not be called.
            } catch (javax.xml.transform.TransformerException te) {
                throw e;
            }
        }
    }

    ////////////////////////////////////////////////////////////////////
    // Internal state.
    ////////////////////////////////////////////////////////////////////

    /**
     * Filter a fatal error event.
     *
     * @param e The error as an exception.
     * @throws SAXException The client may throw
     *                      an exception during processing.
     * @see org.xml.sax.ErrorHandler#fatalError
     */
    public void fatalError(SAXParseException e) throws SAXException {
        if (null != m_errorHandler) {
            try {
                m_errorHandler.fatalError(e);
            } catch (SAXParseException se) {
                // ignore
            }
            // clearCoRoutine(e);
        }

        // This is not great, but we really would rather have the error
        // handler be the error listener if it is a error handler.  Coroutine's fatalError
        // can't really be configured, so I think this is the best thing right now
        // for error reporting.  Possibly another JAXP 1.1 hole.  -sb
        javax.xml.transform.ErrorListener errorListener = m_transformer.getErrorListener();

        if (errorListener instanceof ErrorHandler) {
            ((ErrorHandler) errorListener).fatalError(e);
            if (null != m_errorHandler)
                m_errorHandler.fatalError(e); // may not be called.
        } else {
            try {
                errorListener.fatalError(new javax.xml.transform.TransformerException(e));
                if (null != m_errorHandler)
                    m_errorHandler.fatalError(e); // may not be called.
            } catch (javax.xml.transform.TransformerException te) {
                throw e;
            }
        }
    }

    /**
     * Report the start of DTD declarations, if any.
     *
     * <p>Any declarations are assumed to be in the internal subset
     * unless otherwise indicated by a {@link #startEntity startEntity}
     * event.</p>
     *
     * <p>Note that the start/endDTD events will appear within
     * the start/endDocument events from ContentHandler and
     * before the first startElement event.</p>
     *
     * @param name     The document type name.
     * @param publicId The declared public identifier for the
     *                 external DTD subset, or null if none was declared.
     * @param systemId The declared system identifier for the
     *                 external DTD subset, or null if none was declared.
     * @throws SAXException The application may raise an
     *                      exception.
     * @see #endDTD
     * @see #startEntity
     */
    public void startDTD(String name, String publicId, String systemId)
        throws SAXException {

        if (DEBUG)
            System.out.println("TransformerHandlerImpl#startDTD: " + name + ", "
                + publicId + ", " + systemId);

        if (null != m_lexicalHandler) {
            m_lexicalHandler.startDTD(name, publicId, systemId);
        }
    }

    /**
     * Report the end of DTD declarations.
     *
     * @throws SAXException The application may raise an exception.
     * @see #startDTD
     */
    public void endDTD() throws SAXException {

        if (DEBUG)
            System.out.println("TransformerHandlerImpl#endDTD");

        if (null != m_lexicalHandler) {
            m_lexicalHandler.endDTD();
        }
    }

    /**
     * Report the beginning of an entity in content.
     *
     * <p><strong>NOTE:</entity> entity references in attribute
     * values -- and the start and end of the document entity --
     * are never reported.</p>
     *
     * <p>The start and end of the external DTD subset are reported
     * using the pseudo-name "[dtd]".  All other events must be
     * properly nested within start/end entity events.</p>
     *
     * <p>Note that skipped entities will be reported through the
     * {@link org.xml.sax.ContentHandler#skippedEntity skippedEntity}
     * event, which is part of the ContentHandler interface.</p>
     *
     * @param name The name of the entity.  If it is a parameter
     *             entity, the name will begin with '%'.
     * @throws SAXException The application may raise an exception.
     * @see #endEntity
     * @see org.xml.sax.ext.DeclHandler#internalEntityDecl
     * @see org.xml.sax.ext.DeclHandler#externalEntityDecl
     */
    public void startEntity(String name) throws SAXException {

        if (DEBUG)
            System.out.println("TransformerHandlerImpl#startEntity: " + name);

        if (null != m_lexicalHandler) {
            m_lexicalHandler.startEntity(name);
        }
    }

    /**
     * Report the end of an entity.
     *
     * @param name The name of the entity that is ending.
     * @throws SAXException The application may raise an exception.
     * @see #startEntity
     */
    public void endEntity(String name) throws SAXException {

        if (DEBUG)
            System.out.println("TransformerHandlerImpl#endEntity: " + name);

        if (null != m_lexicalHandler) {
            m_lexicalHandler.endEntity(name);
        }
    }

    /**
     * Report the start of a CDATA section.
     *
     * <p>The contents of the CDATA section will be reported through
     * the regular {@link org.xml.sax.ContentHandler#characters
     * characters} event.</p>
     *
     * @throws SAXException The application may raise an exception.
     * @see #endCDATA
     */
    public void startCDATA() throws SAXException {

        if (DEBUG)
            System.out.println("TransformerHandlerImpl#startCDATA");

        if (null != m_lexicalHandler) {
            m_lexicalHandler.startCDATA();
        }
    }

    /**
     * Report the end of a CDATA section.
     *
     * @throws SAXException The application may raise an exception.
     * @see #startCDATA
     */
    public void endCDATA() throws SAXException {

        if (DEBUG)
            System.out.println("TransformerHandlerImpl#endCDATA");

        if (null != m_lexicalHandler) {
            m_lexicalHandler.endCDATA();
        }
    }

    /**
     * Report an XML comment anywhere in the document.
     *
     * <p>This callback will be used for comments inside or outside the
     * document element, including comments in the external DTD
     * subset (if read).</p>
     *
     * @param ch     An array holding the characters in the comment.
     * @param start  The starting position in the array.
     * @param length The number of characters to use from the array.
     * @throws SAXException The application may raise an exception.
     */
    public void comment(char ch[], int start, int length) throws SAXException {

        if (DEBUG)
            System.out.println("TransformerHandlerImpl#comment: " + start + ", "
                + length);

        if (null != m_lexicalHandler) {
            m_lexicalHandler.comment(ch, start, length);
        }
    }

    /**
     * Report an element type declaration.
     *
     * <p>The content model will consist of the string "EMPTY", the
     * string "ANY", or a parenthesised group, optionally followed
     * by an occurrence indicator.  The model will be normalized so
     * that all whitespace is removed,and will include the enclosing
     * parentheses.</p>
     *
     * @param name  The element type name.
     * @param model The content model as a normalized string.
     * @throws SAXException The application may raise an exception.
     */
    public void elementDecl(String name, String model) throws SAXException {

        if (DEBUG)
            System.out.println("TransformerHandlerImpl#elementDecl: " + name + ", "
                + model);

        if (null != m_declHandler) {
            m_declHandler.elementDecl(name, model);
        }
    }

    /**
     * Report an attribute type declaration.
     *
     * <p>Only the effective (first) declaration for an attribute will
     * be reported.  The type will be one of the strings "CDATA",
     * "ID", "IDREF", "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY",
     * "ENTITIES", or "NOTATION", or a parenthesized token group with
     * the separator "|" and all whitespace removed.</p>
     *
     * @param eName        The name of the associated element.
     * @param aName        The name of the attribute.
     * @param type         A string representing the attribute type.
     * @param valueDefault A string representing the attribute default
     *                     ("#IMPLIED", "#REQUIRED", or "#FIXED") or null if
     *                     none of these applies.
     * @param value        A string representing the attribute's default value,
     *                     or null if there is none.
     * @throws SAXException The application may raise an exception.
     */
    public void attributeDecl(
        String eName, String aName, String type, String valueDefault, String value)
        throws SAXException {

        if (DEBUG)
            System.out.println("TransformerHandlerImpl#attributeDecl: " + eName
                + ", " + aName + ", etc...");

        if (null != m_declHandler) {
            m_declHandler.attributeDecl(eName, aName, type, valueDefault, value);
        }
    }

    /**
     * Report an internal entity declaration.
     *
     * <p>Only the effective (first) declaration for each entity
     * will be reported.</p>
     *
     * @param name  The name of the entity.  If it is a parameter
     *              entity, the name will begin with '%'.
     * @param value The replacement text of the entity.
     * @throws SAXException The application may raise an exception.
     * @see #externalEntityDecl
     * @see org.xml.sax.DTDHandler#unparsedEntityDecl
     */
    public void internalEntityDecl(String name, String value)
        throws SAXException {

        if (DEBUG)
            System.out.println("TransformerHandlerImpl#internalEntityDecl: " + name
                + ", " + value);

        if (null != m_declHandler) {
            m_declHandler.internalEntityDecl(name, value);
        }
    }

    /**
     * Report a parsed external entity declaration.
     *
     * <p>Only the effective (first) declaration for each entity
     * will be reported.</p>
     *
     * @param name     The name of the entity.  If it is a parameter
     *                 entity, the name will begin with '%'.
     * @param publicId The declared public identifier of the entity, or
     *                 null if none was declared.
     * @param systemId The declared system identifier of the entity.
     * @throws SAXException The application may raise an exception.
     * @see #internalEntityDecl
     * @see org.xml.sax.DTDHandler#unparsedEntityDecl
     */
    public void externalEntityDecl(
        String name, String publicId, String systemId) throws SAXException {

        if (DEBUG)
            System.out.println("TransformerHandlerImpl#externalEntityDecl: " + name
                + ", " + publicId + ", " + systemId);

        if (null != m_declHandler) {
            m_declHandler.externalEntityDecl(name, publicId, systemId);
        }
    }
}
